package com.iwdnb.gkgz.application.service.impl;

import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
import java.util.stream.Collectors;

import cn.hutool.core.date.DateUtil;
import com.iwdnb.bmnf.common.utils.BeanConvertUtils;
import com.iwdnb.gkgz.application.service.HistoryDataService;
import com.iwdnb.gkgz.application.service.source.JuHeStockDataSource;
import com.iwdnb.gkgz.application.service.source.NetsStockDataSource;
import com.iwdnb.gkgz.common.utils.StockInitUtils;
import com.iwdnb.gkgz.common.model.dto.Stock;
import com.iwdnb.gkgz.common.model.dto.StockDayData;
import com.iwdnb.gkgz.application.vo.StockType;
import com.iwdnb.gkgz.common.utils.StockUtils;
import com.iwdnb.gkgz.infrastructure.dao.entity.StockDO;
import com.iwdnb.gkgz.infrastructure.dao.entity.StockDayDataDO;
import com.iwdnb.gkgz.infrastructure.dao.repo.StockDayDataRepository;
import com.iwdnb.gkgz.infrastructure.dao.repo.StockRepository;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Service;

/**
 * @author pengl
 * @date 2020/05/03
 **/
@Service
public class HistoryDataServiceImpl implements HistoryDataService {
    public static final Logger logger = LoggerFactory.getLogger(HistoryDataServiceImpl.class);

    private Lock lock = new ReentrantLock();

    @Autowired
    NetsStockDataSource dataSource;

    @Autowired
    JuHeStockDataSource juHeDataSource;

    @Autowired
    StockDayDataRepository repository;

    //    @Autowired
    //    StockDataService stockDataService;

    @Autowired
    TaskExecutor taskExecutor;

    @Autowired
    StockRepository stockRepository;

    @Override
    public List<StockDayData> getDayData(String code, int year) {
        String startYear = String.valueOf(year);
        Date startDate = DateUtil.parse(startYear, "yyyy");
        //        String dateStr = DateUtil.da(date);
        //        String yearStr = date.getYear() + "";

        List<StockDayDataDO> queryList = repository.queryAllByYear(code, startYear);

        //        if(CollectionUtils.isEmpty(queryList)) {
        taskExecutor.execute(new Runnable() {
            @Override
            public void run() {
                loadDataAsync(code, startDate);
            }
        });
        //        }

        return queryList.stream()
            .map(item -> BeanConvertUtils.convert(item, StockDayData.class))
            .collect(Collectors.toList());
    }

    @Override
    public List<StockDayData> getDataByRange(String code, Date startDate, Date endDate) {
        String startDateStr = DateUtil.formatDateTime(startDate);
        String endDateStr = DateUtil.formatDateTime(endDate);

        List<StockDayDataDO> queryList = repository.queryAllByDateRange(code, startDateStr, endDateStr);

        return queryList.stream()
            .map(item -> BeanConvertUtils.convert(item, StockDayData.class))
            .collect(Collectors.toList());
    }

    @Override
    public void calcDataByRange(String code, Date startDate, Date endDate) {
        String startDateStr = DateUtil.formatDateTime(startDate);
        String endDateStr = DateUtil.formatDateTime(endDate);

        List<StockDayDataDO> stockDayDataDOList = repository.queryAllByDateRange(code, startDateStr, endDateStr);
        //if (queryList.size() != 0) {repository.deleteByDateAfterAndDateBefore(startDate, endDate);}
        calStockDayDataResult(stockDayDataDOList);
        repository.saveAll(stockDayDataDOList);
    }

    private BigDecimal calcPercent(BigDecimal start, BigDecimal end) {
        if (start.equals(BigDecimal.ZERO)) {return BigDecimal.ZERO;}
        return end.subtract(start)
            .multiply(BigDecimal.valueOf(100))
            .divide(start, 2, BigDecimal.ROUND_HALF_UP);
    }

    public void calStockDayDataResult(List<StockDayDataDO> allDataList) {
        allDataList.sort(Comparator.comparing(StockDayDataDO::getDate));

        Map<String, Integer> dayDataMap = new HashMap<>();
        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        for (int i = 0; i < allDataList.size(); i++) {
            StockDayDataDO item = allDataList.get(i);
            dayDataMap.put(dateFormat.format(item.getDate()), i);
        }

        for (StockDayDataDO item : allDataList) {
            //前一天收盘价  当日开盘价  开盘时涨幅  当日最高价  最高涨幅  当日最低价  最低涨幅  收盘价 收盘时涨幅 当日振幅
            LocalDate localDate = item.getDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
            Integer curDayIndex = dayDataMap.get(localDate.toString());

            item.setMaxIncreasePer(calcPercent(item.getOpenPrice(), item.getMaxPrice()));
            item.setLowIncreasePer(calcPercent(item.getOpenPrice(), item.getLowPrice()));

            BigDecimal beforeDayClosePrice = BigDecimal.ZERO;
            if (curDayIndex != null && curDayIndex > 0) {
                beforeDayClosePrice = allDataList.get(curDayIndex - 1).getClosePrice();
                item.setYesterdayClosePrice(beforeDayClosePrice);

                item.setOpenIncreasePer(calcPercent(beforeDayClosePrice, item.getOpenPrice()));

                if (item.getOpenIncreasePer().compareTo(BigDecimal.ZERO) < 0
                    && item.getOpenIncreasePer().compareTo(item.getLowIncreasePer()) < 0) {
                    //除权情况
                    item.setOpenIncreasePer(BigDecimal.ZERO);
                }
            } else {
                item.setYesterdayClosePrice(BigDecimal.ZERO);
                item.setOpenIncreasePer(BigDecimal.ZERO);
            }

        }

    }

    @Override
    public StockDayData getDayBeforeData(Date startDate) {
        return null;
    }

    @Override
    public void loadStockDataList(StockType type) {
        taskExecutor.execute(() -> {
            doLoadStockDataList(type);
        });
    }

    @Override
    public void loadStockDayData(String code, String year) {
        List<StockDayData> stockDayDataList = StockUtils.getHistoryDataByYear(code,year);
        for(StockDayData stockDayData:stockDayDataList) {
            StockDayDataDO queryDO = repository.queryDate(stockDayData.getCode(), stockDayData.getDate());
            if (queryDO != null) {
                stockDayData.setId(queryDO.getId());
            }
            repository.save(BeanConvertUtils.convert(stockDayData,StockDayDataDO.class));
        }

    }

    private void doLoadStockDataList(StockType type) {
        if (type == null) {type = StockType.SH;}
        //
        int page = 1;
        int pageSize = 100;
        List<Stock> onlineStockList;

        do {
            onlineStockList = StockInitUtils.queryStockListByType(page, type.getType());
            if (CollectionUtils.isEmpty(onlineStockList)) {
                break;
            }
            List<StockDO> stockDOList = BeanConvertUtils.convertList(onlineStockList, StockDO.class);
            List<String> codeList = stockDOList.stream().map(item -> item.getCode()).collect(Collectors.toList());
            List<StockDO> queryStockDOList = stockRepository.findByCodeIn(codeList);

            Map<String, StockDO> stockDOMap = queryStockDOList.stream().collect(
                Collectors.toMap(StockDO::getCode, Function.identity(), (k1, k2) -> k2));

            for (StockDO stockDO : stockDOList) {
                StockDO existsDO = stockDOMap.get(stockDO.getCode());
                if (existsDO != null) {
                    stockDO.setId(existsDO.getId());
                }
            }

            stockRepository.saveAll(stockDOList);
            logger.info("保存股票基础数据成功:type:{},data:{}", type, stockDOList.size());
            page++;
        } while (onlineStockList != null && onlineStockList.size() != 0);
    }

    private void loadDataAsync(String code, Date date) {
        StockDO stockDO = stockRepository.findByCode(code);
        StockType type = StockType.getValueByType(stockDO.getMarket());
        dataSource.getStockDataByDate(code, type, date, new Date());
    }

}
